{
 "cells": [
  {
   "cell_type": "markdown",
   "source": [
    "# A Circuit with Multiple Qubits"
   ],
   "metadata": {
    "collapsed": false
   },
   "id": "f7f6e61934090d63"
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "outputs": [],
   "source": [
    "from braandket_circuit import CNOT, H, M, NOT, Sequential, allocate_qubits"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-01-25T08:25:59.352315200Z",
     "start_time": "2024-01-25T08:25:55.468971400Z"
    }
   },
   "id": "9fdb245d8b2ff123"
  },
  {
   "cell_type": "markdown",
   "source": [
    "A circuit with two qubits can be defined as below. \n",
    "\n",
    "`H` is the Hadamard gate. `CNOT` is the CNOT (controlled-not) gate. `M` is the measurement (to be clear, projective measurement on computation basics). `Sequential` means that these operations are applied one-by-one as the order in the list.\n",
    "\n",
    "Since a `H` is a single qubit gate, to define which qubit it should be applied on, we use method `.on()`. So does `CNOT` (The first index indicates the control qubit, while the second index indicates the target qubit). Notice that there is no `.on()` after the `M`, because it can be applied on multiple qubits. Writing a bare `M` means that we measure all qubits.\n",
    "\n",
    "Oh, Wait! We haven't defined the number of qubits of this circuit! That's because the `Sequential` defines only the sequence of operations. It doesn't care the number of qubits. In fact, the number of qubits dependents on how many qubits we are about to feed.\n",
    "\n",
    "> this might change in future APIs"
   ],
   "metadata": {
    "collapsed": false
   },
   "id": "3a7c7b46b7cc2274"
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "outputs": [
    {
     "data": {
      "text/plain": "Sequential([\n\tRemapped(HadamardGate(), 0),\n\tRemapped(Controlled(PauliXGate()), 0, 1),\n\tProjectiveMeasurement(),\n])"
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "circuit = Sequential([\n",
    "    H.on(0),\n",
    "    CNOT.on(0, 1),\n",
    "    M\n",
    "])\n",
    "\n",
    "circuit"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-01-25T08:25:59.362878600Z",
     "start_time": "2024-01-25T08:25:59.352315200Z"
    }
   },
   "id": "61e310c337e166b7"
  },
  {
   "cell_type": "markdown",
   "source": [
    "Using method `.on()` we can also define a controlled gate with named argument `control`. So the `CNOT` can be defined as follows."
   ],
   "metadata": {
    "collapsed": false
   },
   "id": "7a917dc643a8e874"
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "outputs": [
    {
     "data": {
      "text/plain": "Sequential([\n\tRemapped(HadamardGate(), 0),\n\tRemapped(Controlled(PauliXGate()), 0, (1,)),\n\tProjectiveMeasurement(),\n])"
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "circuit = Sequential([\n",
    "    H.on(0),\n",
    "    NOT.on(1, control=0),\n",
    "    M\n",
    "])\n",
    "\n",
    "circuit"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-01-25T08:25:59.375009500Z",
     "start_time": "2024-01-25T08:25:59.361879500Z"
    }
   },
   "id": "7bcc9b2d836f4a06"
  },
  {
   "cell_type": "markdown",
   "source": [
    "We can prepare multiple qubits at once by call function `allocate_qubits`."
   ],
   "metadata": {
    "collapsed": false
   },
   "id": "7de04e482478dd5"
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "outputs": [
    {
     "data": {
      "text/plain": "(<BnkParticle name=q_0>, <BnkParticle name=q_1>)"
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "q0, q1 = allocate_qubits(2, name='q')\n",
    "\n",
    "q0, q1"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-01-25T08:25:59.376027Z",
     "start_time": "2024-01-25T08:25:59.368518100Z"
    }
   },
   "id": "52b99f20384bd5f6"
  },
  {
   "cell_type": "markdown",
   "source": [
    "And then we can just call the circuit with multiple qubits and get the result of `M`."
   ],
   "metadata": {
    "collapsed": false
   },
   "id": "32f9c4391b19ff91"
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "result.value=array([1, 1], dtype=int64)\n",
      "result.prob=array(0.5)\n"
     ]
    }
   ],
   "source": [
    "results = circuit(q0, q1)\n",
    "\n",
    "result = results[-1]\n",
    "print(f\"{result.value=}\")\n",
    "print(f\"{result.prob=}\")"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-01-25T08:25:59.428259300Z",
     "start_time": "2024-01-25T08:25:59.373999400Z"
    }
   },
   "id": "2919279bb7b707d7"
  },
  {
   "cell_type": "markdown",
   "source": [
    "As we've talked about above, we can actually call the circuit with more than two qubits.\n",
    "\n",
    "(But calling it with less than two qubits leads to an `IndexError`)"
   ],
   "metadata": {
    "collapsed": false
   },
   "id": "7b4271fb8492d23c"
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "result.value=array([0, 0, 0], dtype=int64)\n",
      "result.prob=array(0.5)\n"
     ]
    }
   ],
   "source": [
    "q0, q1, q2 = allocate_qubits(3, name='q')\n",
    "\n",
    "results = circuit(q0, q1, q2)\n",
    "\n",
    "result = results[-1]\n",
    "print(f\"{result.value=}\")\n",
    "print(f\"{result.prob=}\")"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-01-25T08:25:59.429259200Z",
     "start_time": "2024-01-25T08:25:59.384558900Z"
    }
   },
   "id": "5d20ba8b400fe1c2"
  },
  {
   "cell_type": "markdown",
   "source": [
    "Since the circuit prepares a Bell state. The value of measurement result is either `(0,0)` or `(1,1)`.\n",
    "\n",
    "We'll see this after calling it multiple times. "
   ],
   "metadata": {
    "collapsed": false
   },
   "id": "c26cf9118447da8c"
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "experiment 0:\n",
      "\tresult.value=array([0, 0], dtype=int64)\n",
      "\tresult.prob=array(0.5)\n",
      "experiment 1:\n",
      "\tresult.value=array([0, 0], dtype=int64)\n",
      "\tresult.prob=array(0.5)\n",
      "experiment 2:\n",
      "\tresult.value=array([0, 0], dtype=int64)\n",
      "\tresult.prob=array(0.5)\n",
      "experiment 3:\n",
      "\tresult.value=array([1, 1], dtype=int64)\n",
      "\tresult.prob=array(0.5)\n",
      "experiment 4:\n",
      "\tresult.value=array([1, 1], dtype=int64)\n",
      "\tresult.prob=array(0.5)\n",
      "experiment 5:\n",
      "\tresult.value=array([1, 1], dtype=int64)\n",
      "\tresult.prob=array(0.5)\n",
      "experiment 6:\n",
      "\tresult.value=array([1, 1], dtype=int64)\n",
      "\tresult.prob=array(0.5)\n",
      "experiment 7:\n",
      "\tresult.value=array([0, 0], dtype=int64)\n",
      "\tresult.prob=array(0.5)\n",
      "experiment 8:\n",
      "\tresult.value=array([1, 1], dtype=int64)\n",
      "\tresult.prob=array(0.5)\n",
      "experiment 9:\n",
      "\tresult.value=array([0, 0], dtype=int64)\n",
      "\tresult.prob=array(0.5)\n"
     ]
    }
   ],
   "source": [
    "for i in range(10):\n",
    "    q0, q1 = allocate_qubits(2, name='q')\n",
    "    results = circuit(q0, q1)\n",
    "\n",
    "    result = results[-1]\n",
    "    print(f\"experiment {i}:\")\n",
    "    print(f\"\\t{result.value=}\")\n",
    "    print(f\"\\t{result.prob=}\")"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-01-25T08:25:59.474269700Z",
     "start_time": "2024-01-25T08:25:59.388160500Z"
    }
   },
   "id": "initial_id"
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
